
#import "TestCD.h"
#import "PrefsController.h"

@implementation TestCD

- _printTestBuff
{
    /* call this when testBuff has one (only 1) complete line in it */
    [cmdInfoText replaceSel: testBuff];
    if (consolePrint) printf("mCD %s", testBuff);
    testBuff[0] = '\0';                      

    return self;
}

- awakeFromNib
{
    test_fd = 0;
    [closeFdButton setEnabled:NO];
    consolePrint = NO;  /* should probably set the switch status */
    return self;
}

- setConsolePrint:sender
{
    consolePrint = [sender state];
    return self;
}

- showUsingPrefs:mainPrefObject
{
    mainPrefs = mainPrefObject;
    
    [testCDWindow makeKeyAndOrderFront:self];
    return self;
}

- zOpenFd:sender
{
    strcpy(test_fdName, [mainPrefs rawDeviceName]);

    if ( test_fd > 0 ) close( test_fd );
    test_fd = open( test_fdName, O_RDONLY );
    if ( test_fd < 0 ) {
	NXRunAlertPanel(0,
	    "Error return from open() for device %s",
	    0, 0, 0, test_fdName);
	}
    else {
	[openFdButton setEnabled:NO];
	[closeFdButton setEnabled:YES];
	}
    
    return self;
}

- zCloseFd:sender
{

    if (test_fd > 0 ) {
	close( test_fd );
	test_fd = 0;
	[openFdButton setEnabled:YES];
	[closeFdButton setEnabled:NO];
	}

    return self;
}

- zPlaybackControl:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "pb-ctrl: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_playbackvolume_c9(test_fd, [leftVolField intValue], 
				      [rightVolField intValue], &test_Ereply);
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

- zPlaybackStatus:sender
{
    char *audioStatusText[] = { "playing", "paused", "audio muting on", 
		"play cmd completed", "error during play cmd",
		"no audio play operation requested" };
    char  *statMsg;

    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "pb-stat: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_playbackstatus_c4(test_fd, &cd_playstatus, &test_Ereply);
	statMsg = "*unknown status indicator*";
	if ( cd_playstatus.pbs_audio_status <= PBS_ASTAT_NOREQ )
	         statMsg = audioStatusText[cd_playstatus.pbs_audio_status];
	sprintf (testBuff,
		 "pb-stat: abs block = %u, %02u:%02u:%02u-%02u",
		 cd_playstatus.pbs_logical_block,
		 cd_playstatus.pbs_hour, cd_playstatus.pbs_min,
		 cd_playstatus.pbs_sec, cd_playstatus.pbs_frame );
	[self _printTestBuff];
	sprintf (testBuff,
		 ", audioStatus = %u (%s)\n",
		 cd_playstatus.pbs_audio_status, statMsg );
	[self _printTestBuff];
	sprintf (testBuff,
		 "-------: ch0-sel= %d (vol %03u)  ch1-sel= %d (vol %03u)\n",
		 cd_playstatus.pbs_ch0_sel,
		 cd_playstatus.pbs_ch0_vol,
		 cd_playstatus.pbs_ch1_sel,
		 cd_playstatus.pbs_ch1_vol );
	[self _printTestBuff];
	if ( 0 != (cd_playstatus.pbs_ch2_sel + cd_playstatus.pbs_ch2_vol
	         + cd_playstatus.pbs_ch3_sel + cd_playstatus.pbs_ch3_vol)
		 ) {
	    sprintf (testBuff,
		     "-------: ch2-sel= %d (vol %03u)  ch3-sel= %d (vol %03u)\n",
		     cd_playstatus.pbs_ch2_sel,
		     cd_playstatus.pbs_ch2_vol,
		     cd_playstatus.pbs_ch3_sel,
		     cd_playstatus.pbs_ch3_vol );
	    [self _printTestBuff];
	    }
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

- zModeSelect:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "modesel: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_modeselect_pc_E(test_fd, [leftVolField intValue], 
				   [rightVolField intValue], &test_Ereply);
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

- zModeSense:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "modesns: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_modesense_pc_E(test_fd, &test_volset, &test_Ereply);
	sprintf (testBuff,
		 "modesns: ch0-sel= %d (vol %03u)  ch1-sel= %d (vol %03u)\n",
		 test_volset.ch0_sel,
		 test_volset.ch0_vol,
		 test_volset.ch1_sel,
		 test_volset.ch1_vol );
	[self _printTestBuff];
	if ( 0 != (test_volset.ch2_sel + test_volset.ch2_vol
	         + test_volset.ch3_sel + test_volset.ch3_vol)
		 ) {
	    sprintf (testBuff,
		     "-------: ch2-sel= %d (vol %03u)  ch3-sel= %d (vol %03u)\n",
		     test_volset.ch2_sel,
		     test_volset.ch2_vol,
		     test_volset.ch3_sel,
		     test_volset.ch3_vol );
	    [self _printTestBuff];
	    }
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

- zReadSubCh1:sender
{
#define RSC_P1 1
    char  *statMsg;
    BOOL  wantMsf;
    u_int holdLen, holdAbsBlock, holdRelBlock;

    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "rsc-1: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
        wantMsf = 1;
	do_readsubchannel_42(test_fd, wantMsf, 1, RSC_P1, 0,
			&cd_rscReply1, &test_Ereply);
	statMsg = "*unknown status indicator*";
	switch (cd_rscReply1.scr_header.sch_astatus) {
	   case RSC_ASTAT_PAUSED:
		statMsg = "paused";
		break;
	   case RSC_ASTAT_PLAYING:
		statMsg = "playing";
		break;
	   case RSC_ASTAT_PLAYCOMPLETE:
		statMsg = "play completed";
		break;
	   case RSC_ASTAT_PLAYABORTED:
		statMsg = "play aborted";
		break;
	   case RSC_ASTAT_NONE:
		statMsg = "no status";
		break;
	    }
	holdLen = GET_2BYTE_UINT(cd_rscReply1.scr_header.sch_length);
	sprintf (testBuff,
		 "rsc-1: dc= %u  len= %u  cntrl= %d  audioStatus= %u (%s)\n",
		 cd_rscReply1.u.u_scr_cur_pos.sc1_data_code,
		 holdLen, cd_rscReply1.u.u_scr_cur_pos.sc1_control,
		 cd_rscReply1.scr_header.sch_astatus, statMsg);
	[self _printTestBuff];
	if ( wantMsf ) {
	    sprintf (testBuff,
		    "-----: track= %u  index= %u  absaddr= %02d:%02d:%02d-%02d  reladdr= %02d:%02d:%02d-%02d\n",
		    cd_rscReply1.u.u_scr_cur_pos.sc1_track,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_index,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_abs_addr.msf.hour,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_abs_addr.msf.min,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_abs_addr.msf.sec,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_abs_addr.msf.frame,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_rel_addr.msf.hour,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_rel_addr.msf.min,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_rel_addr.msf.sec,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_rel_addr.msf.frame
		    );
	    [self _printTestBuff];
	    }
	else { /* not MSF, ie, it's LBA format */
	    holdAbsBlock = GET_4BYTE_UINT(cd_rscReply1.u.u_scr_cur_pos.sc1_abs_addr.lba.lblock);
   	    holdRelBlock = GET_4BYTE_UINT(cd_rscReply1.u.u_scr_cur_pos.sc1_rel_addr.lba.lblock);
	    sprintf (testBuff,
		    "-----: track= %u  index= %u  absaddr= %u  reladdr= %u\n",
		    cd_rscReply1.u.u_scr_cur_pos.sc1_track,
		    cd_rscReply1.u.u_scr_cur_pos.sc1_index,
		    holdAbsBlock, holdRelBlock);
	    [self _printTestBuff];
	    }
	if ( 0 != cd_rscReply1.u.u_scr_cur_pos.sc1_reserved1 ) {
	    sprintf (testBuff,
		     "-----: reserved= %d\n",
		     cd_rscReply1.u.u_scr_cur_pos.sc1_reserved1 );
	    [self _printTestBuff];
	    }
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

/* Two CD's which have this field are U2's "Achtung Baby" and
   "Joshua Tree" albums.  So do all the CD's in the "Led Zeppelin
   Complete Studio Recordings" Box set (and all CD's in the set
   have the same media number).  However it seems that *MOST* CD's
   do *not* have this field filled in. */
- zReadSubCh2:sender
{
#define RSC_P2 2
    int  i;
    u_int  holdLen;
    u_char *cptr;

    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "rsc-2: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_readsubchannel_42(test_fd, 1, 1, RSC_P2, 0,
			&cd_rscReply2, &test_Ereply);
	holdLen = GET_2BYTE_UINT(cd_rscReply2.scr_header.sch_length);
	sprintf (testBuff,
		 "rsc-2: dc= %u  audioStatus= %u  len= %u  mcval= %d\n",
		 cd_rscReply2.u.u_scr_med_cat.sc2_data_code,
		 cd_rscReply2.scr_header.sch_astatus, holdLen,
		 cd_rscReply2.u.u_scr_med_cat.sc2_mcval );
	[self _printTestBuff];

	sprintf (testBuff,
		 "-----: media catalog number (UPC)= x' ");

	cptr = &(cd_rscReply2.u.u_scr_med_cat.sc2_med_cat[0]);
	for (i=0; i<15; i++) {
	    char  smallBuff[10] = "       ";
	    sprintf(smallBuff, "%.2X ", *cptr++);  /* ignore comp. warning */
	    strcat(testBuff, smallBuff);
	}
	strcat(testBuff, "'\n");
	[self _printTestBuff];

	if ( 0 != (cd_rscReply2.u.u_scr_med_cat.sc2_reserved1
	         + cd_rscReply2.u.u_scr_med_cat.sc2_reserved2) ) {
	    char  smallBuff[20] = "       ";
	    sprintf (testBuff, "-----: res1= x' ");
	    cptr = &(cd_rscReply2.u.u_scr_med_cat.sc2_med_cat[0]);
	    for (i=0; i<3; i++) {
		sprintf(smallBuff, "%.2X ", *cptr++);
		strcat(testBuff, smallBuff);
	    }
	    sprintf (smallBuff, "'  res2= %d\n",
		     cd_rscReply2.u.u_scr_med_cat.sc2_reserved2 );
	    strcat(testBuff, smallBuff);
	    [self _printTestBuff];
	    }
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

/* One CD which has these fields set for all the tracks is
   U2's "Zooropa" album.  Very few CD's have these fields
   (one field per track) set. */
- zReadSubCh3:sender
{
#define RSC_P3 3
    int  track_number, i;
    u_int  holdLen;
    u_char *cptr;

    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
   
    if ( test_fd == 0 ) {
        sprintf (testBuff, "rsc-3: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	track_number = [trackNumField intValue];
	do_readsubchannel_42(test_fd, 1, 1, RSC_P3, track_number,
			&cd_rscReply3, &test_Ereply);
	
	holdLen = GET_2BYTE_UINT(cd_rscReply3.scr_header.sch_length);
	sprintf (testBuff,
		 "rsc-3: dc= %u  audioStatus = %u  len=%u  track= %u\n",
		 cd_rscReply3.u.u_scr_isrc.sc3_data_code,
		 cd_rscReply3.scr_header.sch_astatus, holdLen,
		 cd_rscReply3.u.u_scr_isrc.sc3_track );
	[self _printTestBuff];
	
	sprintf (testBuff,
		 "-----: tcval= %d  Int.StandRecCode= x' ",
		 cd_rscReply3.u.u_scr_isrc.sc3_tcval);

	cptr = &(cd_rscReply3.u.u_scr_isrc.sc3_isrc[0]);
	for (i=0; i<15; i++) {
	    char  smallBuff[10] = "       ";
	    sprintf(smallBuff, "%.2X ", *cptr++);  /* ignore comp. warning */
	    strcat(testBuff, smallBuff);
	}
	strcat(testBuff, "'\n");
	[self _printTestBuff];

	if ( 0 != (cd_rscReply3.u.u_scr_isrc.sc3_reserved1
	         + cd_rscReply3.u.u_scr_isrc.sc3_reserved2
	         + cd_rscReply3.u.u_scr_isrc.sc3_reserved3) ) {
	    sprintf (testBuff,
		     "-----: reserved fields: #1= %u  #2= %u  #3= %d\n",
		     cd_rscReply3.u.u_scr_isrc.sc3_reserved1,
		     cd_rscReply3.u.u_scr_isrc.sc3_reserved2,
		     cd_rscReply3.u.u_scr_isrc.sc3_reserved3);
	    [self _printTestBuff];
	    }
	}
    
    [cmdInfoText scrollSelToVisible];
    return self;
}

/* referenced by some of the methods that come after it */
- printSenseReply:(struct esense_reply*) snsPtr from:(char *)fromStr
{
    char *senseKeyText[] = { "no error to report", "recovered error",
		"target not ready", "media flaw", "hardware failure",
		"illegal request", "drive attention", "drive access protected",
		"target aborted command", "eom, some data not transfered",
		"source/media data mismatch" };

    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( 0 == (snsPtr->er_ibvalid + snsPtr->er_class
	     + snsPtr->er_code + snsPtr->er_sensekey
	     + snsPtr->er_addsensecode + snsPtr->er_qualifier ) ) {
	sprintf (testBuff,
		 "SenseInfo: no error from the %s\n", fromStr);
	[self _printTestBuff];
	[cmdInfoText scrollSelToVisible];
	return self;
	}

    sprintf (testBuff,
		"SenseInfo: sense returned from the %s:\n", fromStr);
    [self _printTestBuff];
    sprintf (testBuff,
	     "---------: InfoBytesValid= %d  errClass= %d  errCode= %d  SenseKey= %d (",
	     snsPtr->er_ibvalid, snsPtr->er_class, snsPtr->er_code,
	     snsPtr->er_sensekey);
    if ( snsPtr->er_sensekey > SENSE_MISCOMPARE ) {
	strcat(testBuff, "*unknown*");
	}
    else {
	strcat(testBuff, senseKeyText[snsPtr->er_sensekey]);
	}
	strcat(testBuff, ")\n");
    [self _printTestBuff];

    if ( snsPtr->er_ibvalid ) {
#if	__BIG_ENDIAN__
	sprintf (testBuff,
		 "---------: InfoBytes= x'%.2X %.6X'\n",
		    snsPtr->er_infomsb, snsPtr->er_info );
#elif	__LITTLE_ENDIAN__
	sprintf (testBuff,
		 "---------: InfoBytes= x'%.2X %.2X %.2X %.2X'\n",
		    snsPtr->er_info3, snsPtr->er_info2,
		    snsPtr->er_info1, snsPtr->er_info0);
#else
#error	SCSI command / data structures are compiler sensitive
#endif
	[self _printTestBuff];
	}

    sprintf (testBuff,
	     "---------: addSenseCode= %d  addSenseQualifier= %d\n",
	     snsPtr->er_addsensecode,
	     snsPtr->er_qualifier);
    [self _printTestBuff];

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zPrintLastReqSense:sender
{
    [self printSenseReply: &test_Ereply from:"last command tested"];
    return self;
}

- zPrintMainReqSense:sender
{
    [self printSenseReply: [mainObject esenseReplyPtr]
		    from:"last main program command"];
    return self;
}

- zPrintMainCurPosSense:sender
{
    [self printSenseReply: [mainObject esenseCurPosPtr]
		    from:"last main program call to readcurrrentposition"];
    return self;
}

- zPrintMainTestUnitSense:sender
{
    [self printSenseReply: [mainObject esenseTestUnitPtr]
		    from:"last main program call to testunitready"];
    return self;
}

- zAllowRemoval:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
   
    if ( test_fd == 0 ) {
        sprintf (testBuff, "allow: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_preventremoval_1e(test_fd, NO, &test_Ereply);
	}

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zPreventRemoval:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
   
    if ( test_fd == 0 ) {
        sprintf (testBuff, "prevent: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_preventremoval_1e(test_fd, YES, &test_Ereply);
	}

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zPlayALittle:sender;
{
    struct pa_msf tst_start = {1, 0, 0};
    struct pa_msf tst_end = {2, 0, 0};
    
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    if ( test_fd == 0 ) {
        sprintf (testBuff, "playing: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	sprintf (testBuff, "playing: start %02u:%02u-%02u",
		    tst_start.min,
		    tst_start.sec, tst_start.frame);
	[self _printTestBuff];
	sprintf (testBuff, " - end %02u:%02u-%02u\n",
		    tst_end.min,
		    tst_end.sec, tst_end.frame);
	[self _printTestBuff];
    
	do_playaudio_msf_47(test_fd, tst_start, tst_end, &test_Ereply);
	}

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zPausePlay:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
   
    if ( test_fd == 0 ) {
        sprintf (testBuff, "pause: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_pauseaudio_4b(test_fd, YES, &test_Ereply);
	}

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zResumePlay:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
   
    if ( test_fd == 0 ) {
        sprintf (testBuff, "resume: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_pauseaudio_4b(test_fd, NO, &test_Ereply);
	}

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zRezeroUnit:sender
{
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
   
    if ( test_fd == 0 ) {
        sprintf (testBuff, "rezerounit: no open fd to test it on\n");
	[self _printTestBuff];
	}
    else {
	do_rezerounit_01(test_fd, &test_Ereply);
	}

    [cmdInfoText scrollSelToVisible];
    return self;
}

- zInquiryAllScsi:sender
{
    char *devTypeText[] = { "R/W disk", "tape", "printer", 
		"processor", "write-once optical", "CD-ROM, etc",
		"scanner", "optical storage", "jukebox changer",
		"communication device", "graphic A", "graphic B",
		"logical unit not present" };
    struct inquiry_reply *mInq;
    int	indx;
    
    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];

    sprintf (testBuff, " NOTE: the inquiry-all operation may cause your machine\n");
    [self _printTestBuff];
    sprintf (testBuff, "       to \"freeze up\" for as long as a minute\n");
    [self _printTestBuff];
    [cmdInfoText scrollSelToVisible];

    do_inquiryall( INQ_ALL_MAX_SCSI_CNT, &test_InqAll, &test_Ereply);

    [cmdInfoText setSel: [cmdInfoText textLength] :[cmdInfoText textLength]];
    sprintf (testBuff, "     inquiry_all max scsi count = %d\n",
			test_InqAll.maxScsiCount );
    [self _printTestBuff];

    for(indx = 0; indx < test_InqAll.maxScsiCount; indx++) {
	mInq = &(test_InqAll.scsiArray[indx].inqResult);
    
	if ( test_InqAll.scsiArray[indx].devIoStat ) {
	    sprintf (testBuff,
			"SCSI #%d: no device attached with this SCSI id\n",
			indx );
	    [self _printTestBuff];
	    }
	else {
	    sprintf (testBuff,
	    	    "SCSI #%d: /dev/rsd%dh  removable= %d  devicetype= %d (%s)\n",
		    indx, test_InqAll.scsiArray[indx].deviceNumber,
		    mInq->ir_removable,
		    mInq->ir_devicetype, devTypeText[mInq->ir_devicetype] );
	    [self _printTestBuff];
	    sprintf (testBuff,
			"---- #%d:    vendorid= %.8s  productid= %.16s  revision= %.4s\n",
			indx, mInq->ir_vendorid, mInq->ir_productid,
			mInq->ir_revision);
	    [self _printTestBuff];
	    sprintf (testBuff,
	    	    "---- #%d:    qual= %d  tqual = %d - versions ISO= %d ECMA= %d ANSI= %d - fmt= %d  addlen= %d\n",
		    indx,
		    mInq->ir_qual,
		    mInq->ir_typequalifier, mInq->ir_isoversion,
		    mInq->ir_ecmaversion, mInq->ir_ansiversion,
		    mInq->ir_rspdatafmt, mInq->ir_addlistlen );
	    [self _printTestBuff];
	    if (mInq->ir_misc[0] != '\0') {
		sprintf (testBuff,
			 "---- #%d:    misc info= %.28s\n",
			 indx, mInq->ir_misc);
		[self _printTestBuff];
		}
	    }
	}		/* end for (indx =... */

    [cmdInfoText scrollSelToVisible];
    return self;
}


@end
